{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Use OSMnx to get street networks by place name\n",
    "\n",
    "Author: [Geoff Boeing](https://geoffboeing.com/)\n",
    "\n",
    "  - [Documentation](https://osmnx.readthedocs.io/)\n",
    "  - [Journal article and citation info](https://geoffboeing.com/publications/osmnx-paper/)\n",
    "  - [Code repository](https://github.com/gboeing/osmnx)\n",
    "  - [Examples gallery](https://github.com/gboeing/osmnx-examples)\n",
    "  \n",
    "Once you've perused the features demo and overview notebooks, this notebook provides further demonstration of querying by place name with OSMnx."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import osmnx as ox\n",
    "\n",
    "ox.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OSMnx lets you download street network data and build topologically corrected multidigraphs, project to UTM and plot the networks, and save the street network as SVGs, GraphML files, or GeoPackages for later use. The street networks are directed and preserve one-way directionality. API responses can be cached locally so OSMnx doesn't have to request the same data from the API multiple times: saving bandwidth, increasing speed, and enabling reproducibility.\n",
    "\n",
    "You can download a street network by providing OSMnx any of the following:\n",
    "  - a bounding box\n",
    "  - a lat-long point plus a distance (either distance along the network, or cardinal)\n",
    "  - an address plus a distance (either distance along the network, or cardinal)\n",
    "  - a place name or list of place names (for OSMnx to automatically geocode and get the boundary of)\n",
    "  - a polygon of the desired street network's boundaries\n",
    "\n",
    "You can also specify several different built-in network types:\n",
    "  - `drive` - get drivable public streets (but not service roads)\n",
    "  - `drive_service` - get drivable streets, including service roads\n",
    "  - `walk` - get all streets and paths that pedestrians can use (this network type ignores one-way directionality)\n",
    "  - `bike` - get all streets and paths that cyclists can use\n",
    "  - `all` - download all non-private OSM streets and paths\n",
    "  - `all_private` - download all OSM streets and paths, including private-access ones\n",
    "\n",
    "Or you can define your own fine-tuned network type using OSMnx's `custom_filter` parameter (to get just highways, or railways, canals, etc).\n",
    "\n",
    "## 1. Query/download place boundaries\n",
    "\n",
    "The `graph_from_place` function uses place boundary geocoding logic to find the network within your place's boundary. Let's look briefly at this place boundary querying before we get to street networks. You can download by cities, neighborhoods, boroughs, counties, states, or countries: anything with geocodable polygon boundaries in OSM's database. Notice the polygon geometries represent political boundaries, not physical/land boundaries. OSMnx will turn your geocoded place (or multiple places) boundaries into a geopandas GeoDataFrame."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# neighborhoods or boroughs\n",
    "gdf = ox.geocoder.geocode_to_gdf(\"Manhattan, New York, New York, USA\")\n",
    "\n",
    "# counties\n",
    "gdf = ox.geocoder.geocode_to_gdf(\"Cook County, Illinois, United States\")\n",
    "\n",
    "# states\n",
    "gdf = ox.geocoder.geocode_to_gdf(\"Iowa\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# you can get multiple countries in a single query\n",
    "gdf = ox.geocoder.geocode_to_gdf([\"United Kingdom\", \"Ireland\"])\n",
    "\n",
    "# or multiple cities\n",
    "places = [\n",
    "    \"Berkeley, California, USA\",\n",
    "    \"Oakland, California, USA\",\n",
    "    \"Piedmont, California, USA\",\n",
    "    \"Emeryville, California, USA\",\n",
    "    \"Alameda, Alameda County, CA, USA\",\n",
    "]\n",
    "gdf = ox.geocoder.geocode_to_gdf(places)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The `geocode_to_gdf` function takes a `which_result` argument. Its default value `None` makes OSMnx retrieve the first result with geometry type Polygon/MultiPolygon (if one exists on OSM). Alternatively, pass an integer value as `which_result` to retrieve a specific geocoding result, regardless of its geometry type.\n",
    "\n",
    "When querying, be specific and explicit, and sanity check the results. Try passing a dict instead of a string to be more explicit."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# oops, this gets the county of alameda rather than the city!\n",
    "alameda1 = ox.geocoder.geocode_to_gdf(\"Alameda, California, USA\")\n",
    "\n",
    "# this gets the city of alameda\n",
    "alameda2 = ox.geocoder.geocode_to_gdf(\n",
    "    {\n",
    "        \"city\": \"Alameda\",\n",
    "        \"county\": \"Alameda County\",\n",
    "        \"state\": \"California\",\n",
    "        \"country\": \"USA\",\n",
    "    }\n",
    ")\n",
    "\n",
    "# the city is a very small part of the county\n",
    "alameda1 = ox.projection.project_gdf(alameda1)\n",
    "alameda2 = ox.projection.project_gdf(alameda2)\n",
    "alameda2.area.iloc[0] / alameda1.area.iloc[0]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "OSM resolves 'Mexico' to Mexico City (as the first geocoding result) and returns a single point at the center of the city. If we want the boundaries of the country of Mexico, we can 1) specify which_result=None to find the first polygon and hope it's the country, or 2) pass a dict containing a structured query to specify that we want Mexico the country instead of Mexico the city."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "mexico = ox.geocoder.geocode_to_gdf(\"Mexico\", which_result=2)\n",
    "type(mexico[\"geometry\"].iloc[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# let the geocoder find the first Polygon/MultiPolygon result\n",
    "mexico = ox.geocoder.geocode_to_gdf(\"Mexico\", which_result=None)\n",
    "type(mexico[\"geometry\"].iloc[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# instead of a string, you can pass a dict containing a structured query for better precision\n",
    "mexico = ox.geocoder.geocode_to_gdf({\"country\": \"Mexico\"})\n",
    "type(mexico[\"geometry\"].iloc[0])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# you can pass multiple queries with mixed types (dicts and strings)\n",
    "mx_gt_tx = ox.geocoder.geocode_to_gdf([{\"country\": \"Mexico\"}, \"Guatemala\", {\"state\": \"Texas\"}])\n",
    "mx_gt_tx = ox.projection.project_gdf(mx_gt_tx)\n",
    "ax = mx_gt_tx.plot(fc=\"gray\", ec=\"w\")\n",
    "_ = ax.axis(\"off\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If you query 'France', OSM returns the country with all its overseas territories as result 1 and Metropolitan France alone as later down the results. Passing `which_result` can help you specifically retrieve the desired geocoding result."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "france = ox.geocoder.geocode_to_gdf(\"France\")\n",
    "france = ox.projection.project_gdf(france)\n",
    "ax = france.plot(fc=\"gray\", ec=\"none\")\n",
    "_ = ax.axis(\"off\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Finally, note that you can also retrieve an element by its OSM ID rather, than trying to geocode a place name, by passing `by_osmid=True` to the function. See documentation for usage details."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "france = ox.projection.project_gdf(ox.geocoder.geocode_to_gdf(\"R1403916\", by_osmid=True))\n",
    "ax = france.plot(fc=\"gray\", ec=\"none\")\n",
    "_ = ax.axis(\"off\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Get street networks by place name\n",
    "\n",
    "This \"by place\" querying logic works the same as the place boundary querying we just saw above."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get the walking network for piedmont\n",
    "G = ox.graph.graph_from_place(\"Piedmont, California, USA\", network_type=\"walk\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or get the walking network within a 500 meter buffer of piedmont\n",
    "gdf = ox.geocoder.geocode_to_gdf(\"Piedmont, CA, USA\")\n",
    "polygon = ox.utils_geo.buffer_geometry(gdf.iloc[0][\"geometry\"], 500)\n",
    "G = ox.graph.graph_from_polygon(polygon, network_type=\"walk\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a network from multiple places\n",
    "places = [\n",
    "    \"Piedmont, California, USA\",\n",
    "    {\"city\": \"Berkeley\", \"state\": \"California\"},\n",
    "    \"Emeryville, California, USA\",\n",
    "]\n",
    "\n",
    "# use retain_all to keep all disconnected subgraphs (e.g. if your places aren't contiguous)\n",
    "G = ox.graph.graph_from_place(places, network_type=\"drive\", retain_all=True)\n",
    "fig, ax = ox.plot.plot_graph(G, node_size=0, edge_color=\"#FFFF5C\", edge_linewidth=0.25)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# or create a network from structured place queries\n",
    "places = [\n",
    "    {\"city\": \"Daly City\", \"state\": \"California\"},\n",
    "    {\"city\": \"South San Francisco\", \"state\": \"California\"},\n",
    "]\n",
    "G = ox.graph.graph_from_place(places, network_type=\"drive\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get the network for the borough of manhattan\n",
    "G = ox.graph.graph_from_place(\"Manhattan, New York, New York, USA\", network_type=\"drive\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# get the network for a neighborhood\n",
    "place = \"Chinatown, San Francisco, California\"\n",
    "G = ox.graph.graph_from_place(place, network_type=\"drive\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%time\n",
    "# get the network for all of LA\n",
    "# takes a couple minutes to do all the downloading and processing\n",
    "# retain_all=True means we'll keep all the disconnected graph components\n",
    "# simplify=False means we won't simplify the graph topology\n",
    "place = \"Los Angeles, California, USA\"\n",
    "G = ox.graph.graph_from_place(place, network_type=\"drive\", simplify=False, retain_all=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# create a network constrained to the shape of hong kong island\n",
    "G = ox.graph.graph_from_place(\"Hong Kong Island\", network_type=\"drive\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python (ox)",
   "language": "python",
   "name": "ox"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
